;功能：实现字符串输入显示、回退删除功能（304页）
;方法：
;   定义数据段data数据标号str，用以存储键盘输入的字符
;   定义数据段data数据标号str_num，用以存储键盘输入字符计数
;   初始化es(显示缓冲区段地址0b800h)、di(屏幕中间偏移地址)
;   初始化ds(数据段data段地址)、bx(数据段data偏移地址)
;   主函数input_str循环
;       ㊀清空键盘缓存区
;       ㊁调用16h中断0号功能，读取键盘缓冲区的内容
;       ㊂比较键盘输入的ASCII码是否小于20h
;           <20h，不可显示的特殊字符
;               比较键盘输入的键盘码是否等于0eh、1ch
;                   =0eh，键盘输入是退格键
;                       比较字符计数bx是否为0
;                       重置data段保存的最后输入的字符为0
;                       将屏幕上最后输入的字符清空
;                       循环跳转至主函数input_str
;                   =1ch，键盘输入是回车键
;                       跳出主函数input_str
;       ㊃判断字符个数是否超出data段临界。超出，自动中断循环
;       ㊄>=20h，可显示字符
;           调用char_push函数，依次向data段写入字符
;           调用show_str函数，向屏幕输出字符
;               依次将data段的字符输出屏幕
;               循环跳转至主函数input_str
;自评：采用直接定址表，代码冗乱不堪（对方法2的更改）
assume cs:code

data segment
    str db 5 dup (0)
    str_num dw 0
data ends

stack segment
    db 128 dup (0)
stack ends

code segment
start:
    mov bx,stack
    mov ss,bx
    mov sp,128

    mov bx,0b800h
    mov es,bx
    mov di,160*12+40*2
    mov bx,data
    mov ds,bx
    mov bx,offset str
    
    call input_str

    mov ax,4c00h
    int 21h

input_str:
    call clear_buf
    mov ah,0
    int 16h

    ;ah扫描码，al ASCII码
    ;比较键盘输入的ASCII码是否小于20h
    ;<20h，不可显示的特殊字符
    cmp al,20h
    jb no_char

    ;判断键盘输入的个数是否超过data段的临界值
    cmp str_num,4
    je input_str_ret

    mov dh,1
    call str_func
    mov dh,2
    call str_func
    jmp input_str
input_str_ret:
    ret
no_char:
    cmp ah,0eh
    ;退格键
    je backspace
    cmp ah,1ch
    ;回车键，退出字符串输入的主函数循环
    je input_str_ret
    jmp input_str
backspace:
    ;比较字符计数str_num是否为0
    cmp str_num,0
    je input_str
    mov dh,0
    call str_func
    jmp input_str


;====功能：字符串函数====
;====参数：dh 0删除/1输入/2显示====
;====返回值：无====
str_func:
    jmp short str_func_start
    ;存储子程序入口地址的直接定址表
    table dw str_pop, str_push, str_show
str_func_start:
    ;写成push bx，以bx定位子程序入口，报错？
    push si
    push cx
    cmp dh,2
    ja str_func_ret
    mov cl,8
    ;等价于mov dl,dh   mov dh,0
    shr dx,cl
    ;等价于add dx,dx
    shl dx,1
    mov si,dx
    call word ptr table[si]
str_func_ret:
    pop cx
    pop si
    ret


;====删除输入的键盘字符====
str_pop:
    push bx
    push di
    dec str_num
    add bx,str_num
    ;重置data段保存的最后输入的字符为0
    mov byte ptr ds:[bx],0
    add di,str_num
    add di,str_num
    ;将屏幕上最后输入的字符清空
    mov byte ptr es:[di],' '
    pop di
    pop bx
    ret    


;====将键盘输入写入data段====
str_push:
    push bx
    add bx,str_num
    mov ds:[bx],al
    inc ds:[str_num]
    pop bx
    ret


;====将用户的键盘输入显示于屏幕====
str_show:
    push bx
    push di
str_show_loop:
    cmp byte ptr ds:[bx],0
    je str_show_ret
    mov al,ds:[bx]
    mov es:[di],al
    mov byte ptr es:[di+1],02h
    add di,2
    inc bx
    jmp short str_show_loop
str_show_ret:
    pop di
    pop bx
    ret


clear_buf:
    mov ah,1
    int 16h
    jz clear_buf_ret
    mov ah,0
    int 16h
    jmp clear_buf
clear_buf_ret:
    ret

code ends
end start